<?php

namespace StudyBuddy;

abstract class Output {

    /**
     * Actual data that is going to be
     * formatted into json or xml
     * by this object
     *
     * @var array
     */
    protected $aData;

    /**
     * Formatted string, this value
     * is set from the format() function
     * by converting $this->aData into the formatted
     * string
     *
     * @var string
     */
    protected $formatted;

    /**
     * Factory method
     *
     * @param string $output either 'json' or 'xml'
     * @param string $callback in case of jsonp this is an additional
     * callback. Body of response will be wrapped in this callback function
     *
     * @return object of this class (sub-class if $output is 'xml')
     */
    public static function factory($output = 'json', $callback = null) {
        d('cp');
        switch ($output) {
            case 'json':
                d('cp');
                return new OutputJson();
                break;

            case 'jsonp':
                return new OutputJsonp((string) $callback);
                break;
        }

        exit('Unsupported output format: ' . (string) $output);
    }

    /**
     * Must return correct content type
     * for the output (formatted) format of the sub-class
     * For example: 'application/json' for json
     * or 'text/xml' for xml
     *
     * @return string
     */
    abstract public function getContentType();

    /**
     * Set data to be formatted
     *
     * @param mixed array | MongoCursor
     * @throws DevException if input not array and not Traversable interface
     * (MongoCursor is an Iterator which is Traversable)
     *
     */
    public function setData($data) {
        if (!\is_array($data) && (!is_object($data) || !($data instanceof \Traversable))) {
            throw new DevException('Invalid data type of $data');
        }

        if ($data instanceof \MongoCursor) {
            $data = \iterator_to_array($data);
        }



        $this->aData = $data;
    }

    /**
     * Getter for aData
     * 
     * @return array
     */
    public function getData() {
        return $this->aData;
    }

    /**
     * Format the data and return the result string
     *
     * @return string
     * @throws DevException if $this->aData is not array or traversable
     * (Traversable object can be used with "foreach" like an array)
     */
    public function getFormatted() {

        if (!isset($this->aData)) {
            throw new DevException('Data has not been set yet. ');
        }

        if (!\is_array($this->aData) && !($this->aData instanceof \Traversable)) {
            throw new DevException('invalid data type of $data');
        }

        return $this->format($this->aData);
    }

    /**
     * Must format $this->aData into
     * json or xml format, depending on concrete implementation
     *
     * @return string formatted string
     */
    abstract protected function format();

    /**
     *
     * Delegate to getFormatted
     *
     * @return string
     * @see getFormattedString
     */
    public function __toString() {
        return $this->getFormatted();
    }

}
